using System;
using System.Collections;
using System.Collections.Generic;
using Server;
using Server.Items;
using Server.Mobiles;
using Server.Gumps;
using Server.Prompts;
using Server.Targeting;
using Server.Misc;
using Server.Multis;
using Server.ContextMenus;


namespace Server.Mobiles
{
	public class CommissionedVendor : PlayerVendor, IHardsellable
	{
		// whether to allow players to place a commissioned vendor at home
		// whether to enable the player vendor backpack
		// whether mall tiles are automatically added on vendor dismissal
		public static bool AllowPlaceHome = true;
		public static bool EnableVendorBackpack = true;
		public static bool AutoAddTileOnDismiss = true;

		private VendorManager m_Manager;
		private Container m_HoldBox;
		private Container m_ShowBox;
		private Type m_Currency;
		private int m_CommRate;
		private bool m_IsComm;
		private bool m_AllowBid;
		private bool m_BondOnly;
		private int m_LastHold;

		private CurrencyInfo m_CInfo;

		private Hashtable m_ShopItems;
		private List<BidByItem> m_BidItems;

		// to change m_Delay to double such that GMs can change it
		private Timer m_Timer;
		private TimeSpan m_Delay;


		#region IHardsellable
		[CommandProperty( AccessLevel.GameMaster)]
		public bool IsComm
		{
			get{ return m_IsComm; }
			set{ m_IsComm = value; }
		}

		[CommandProperty( AccessLevel.GameMaster)]
		public int CommRate
		{
			get{ return m_CommRate; }
			set{ m_CommRate = value; }
		}

		public Container HoldBox
		{
			get{ return m_HoldBox; }
			set{ m_HoldBox = value; InvalidateProperties(); }
		}

		public Container ShowBox
		{
			get{ return m_ShowBox; }
			set{ m_ShowBox = value; InvalidateProperties(); }
		}

		[CommandProperty( AccessLevel.GameMaster)]
		public VendorManager Manager
		{
			get{ return m_Manager; }
			set{ m_Manager = value; InvalidateProperties(); }
		}

		public Hashtable ShopItems
		{
			get{ return m_ShopItems; }
			set{ m_ShopItems = value; InvalidateProperties(); }
		}

		public Type Currency
		{
			get{ return m_Currency; }
			set{ m_Currency = value; InvalidateProperties(); }
		}

		public Timer CommTimer
		{
			get{ return m_Timer; }
		}

		[CommandProperty( AccessLevel.GameMaster)]
		public double CommDelay
		{
			get{ return m_Delay.TotalMinutes; }
			set{ m_Delay = TimeSpan.FromMinutes( value ); }
		}

		public CurrencyInfo CInfo
		{
			get{ return m_CInfo; }
			set{ m_CInfo = value; InvalidateProperties(); }
		}

		[CommandProperty( AccessLevel.GameMaster)]
		public bool AllowBid
		{
			get{ return ( m_AllowBid && m_Manager != null && m_Manager.AllowBid ); }
			set
			{
				if ( m_Manager != null && m_Manager.AllowBid )
					m_AllowBid = value;
				else
					m_AllowBid = false;

				InvalidateProperties();
			}
		}

		[CommandProperty( AccessLevel.GameMaster)]
		public bool BondOnly
		{
			get{ return m_BondOnly; }
			set{ m_BondOnly = value; InvalidateProperties(); }
		}

		public List<BidByItem> BidItems
		{
			get{ return m_BidItems; }
			set{ m_BidItems = value; InvalidateProperties(); }
		}
		#endregion

		[CommandProperty( AccessLevel.GameMaster)]
		public int LastHold
		{
			get{ return m_LastHold; }
			set{ m_LastHold = value; InvalidateProperties(); }
		}

		public virtual int GetShoeHue()
		{
			if ( 0.1 > Utility.RandomDouble() )
				return 0;

			return Utility.RandomNeutralHue();
		}

		public virtual VendorShoeType ShoeType
		{
			get { return VendorShoeType.Shoes; }
		}

		public virtual bool IsInvulnerable { get { return true; } }


		public CommissionedVendor( Mobile owner, BaseHouse house ) : base( owner, house )
		{
			Owner = owner;
			House = house;

			BankAccount = 10;
			HoldGold = 10;
			Blessed = true;
			IsComm = true;
			CommRate = 5;
			m_AllowBid = true;
			m_BondOnly = true;
			m_LastHold = HoldGold;

			m_BidItems = new List<BidByItem>();

			m_ShopItems = new Hashtable();
			m_Currency = typeof( Gold );

			m_CInfo = new CurrencyInfo( 0xEEF, 0, false );

			m_ShowBox = new VendorBox();
			m_ShowBox.Movable = false;
			AddItem( m_ShowBox );

			m_HoldBox = new VendorBox();
			m_HoldBox.Movable = false;
			AddItem( m_HoldBox );

			InitVendorBody();
			InitVendorOutfit();

			m_Delay = GetCommDelay();

			if ( m_IsComm )
			{
				if ( DailyPayTimer != null )
					DailyPayTimer.Stop();

				DailyPayTimer = new Timer( TimeSpan.FromDays( 9999.0 ) );

				m_Timer = new InternalTimer( (Mobile)this, m_Delay );
				m_Timer.Start();
			}
		}

		public CommissionedVendor( Serial serial ) : base( serial )
		{
		}


		public void InitVendorBody()
		{
			SpeechHue = Utility.RandomDyedHue();
			Hue = Utility.RandomSkinHue();

			if ( IsInvulnerable && !Core.AOS )
				NameHue = 0x35;

			if ( Female = GetGender() )
			{
				Body = 0x191;
				Name = NameList.RandomName( "female" );
			}
			else
			{
				Body = 0x190;
				Name = NameList.RandomName( "male" );
			}
		}

		public virtual bool GetGender()
		{
			return Utility.RandomBool();
		}

		public virtual int GetHairHue()
		{
			return Utility.RandomHairHue();
		}

		public void InitVendorOutfit()
		{
			switch ( Utility.Random( 3 ) )
			{
				case 0: AddItem( new FancyShirt( GetRandomHue() ) ); break;
				case 1: AddItem( new Doublet( GetRandomHue() ) ); break;
				case 2: AddItem( new Shirt( GetRandomHue() ) ); break;
			}

			switch ( ShoeType )
			{
				case VendorShoeType.Shoes: AddItem( new Shoes( GetShoeHue() ) ); break;
				case VendorShoeType.Boots: AddItem( new Boots( GetShoeHue() ) ); break;
				case VendorShoeType.Sandals: AddItem( new Sandals( GetShoeHue() ) ); break;
				case VendorShoeType.ThighBoots: AddItem( new ThighBoots( GetShoeHue() ) ); break;
			}

			int hairHue = GetHairHue();

			Utility.AssignRandomHair( this, hairHue );
			Utility.AssignRandomFacialHair( this, hairHue );

			if ( Female )
			{
				switch ( Utility.Random( 6 ) )
				{
					case 0: AddItem( new ShortPants( GetRandomHue() ) ); break;
					case 1:
					case 2: AddItem( new Kilt( GetRandomHue() ) ); break;
					case 3:
					case 4:
					case 5: AddItem( new Skirt( GetRandomHue() ) ); break;
				}
			}
			else
			{
				switch ( Utility.Random( 2 ) )
				{
					case 0: AddItem( new LongPants( GetRandomHue() ) ); break;
					case 1: AddItem( new ShortPants( GetRandomHue() ) ); break;
				}
			}
		}

		public virtual int GetRandomHue()
		{
			switch ( Utility.Random( 5 ) )
			{
				default:
				case 0: return Utility.RandomBlueHue();
				case 1: return Utility.RandomGreenHue();
				case 2: return Utility.RandomRedHue();
				case 3: return Utility.RandomYellowHue();
				case 4: return Utility.RandomNeutralHue();
			}
		}


		public override void OnAfterDelete()
		{
			if ( m_Timer != null )
				m_Timer.Stop();

			base.OnAfterDelete();
		}

		public override void OnDelete()
		{
			if ( SaveStone.TradePets != null && SaveStone.TradePets.Count > 0 )
			{
				for ( int i = 0; i < SaveStone.TradePets.Count; i++ )
				{
					TradeMobile tm = SaveStone.TradePets[i] as TradeMobile;

					if ( tm.m_owner == null || tm.m_owner.Deleted )
					{
						Timer.DelayCall( TimeSpan.Zero, new TimerStateCallback( RemovePet_Callback ), new object[] { tm.m_pet } );
						// TradePets will not be removed here, because there might not be enough time
						// They will be removed in Maintenance.cs
//						SaveStone.TradePets.RemoveAt( i-- );
					}
					else if ( tm.m_vendor == null || tm.m_vendor.Deleted )
					{
						tm.m_pet.ControlTarget = null;
						tm.m_pet.ControlOrder = OrderType.Stay;
						tm.m_pet.Internalize();

						tm.m_pet.SetControlMaster( null );
						tm.m_pet.SummonMaster = null;
						tm.m_pet.IsStabled = true;
						tm.m_pet.Loyalty = BaseCreature.MaxLoyalty; // Wonderfully happy
						tm.m_owner.Stabled.Add( tm.m_pet );

						SaveStone.TradePets.RemoveAt( i-- );
					}

				}
			}

			base.OnDelete();
		}

		private static void RemovePet_Callback( object state )
		{
			object[] aState = (object[]) state;

			BaseCreature pet = (BaseCreature) aState[0];

			pet.Internalize();
			pet.Delete();
		}

		public override void Dismiss( Mobile from )
		{
			if ( m_HoldBox != null && m_HoldBox.Items.Count > 0 )
			{
				SayTo( from, 1038325 ); // You cannot dismiss me while I am holding your goods.
				return;
			}
			else if ( m_ShowBox != null && m_ShowBox.Items.Count > 0 )
			{
				List<Item> demos = m_ShowBox.Items;
				bool found = false;
	
				if ( demos != null && demos.Count > 0 )
				{
					for ( int i = 0; i < demos.Count; i++ )
					{
						Item demo = demos[i];
	
						if ( !( demo is CloneItem ) )
							found = true;
					}
				}

				if ( found )
				{
					SayTo( from, 1038325 ); // You cannot dismiss me while I am holding your goods.
					return;
				}

				RemoveItem( m_ShowBox );
			}

			// this check is required, or else tiles may be created even when vendor not dismissed
			Container pack = this.Backpack;

			if ( pack != null && pack.Items.Count > 0 )
			{
				SayTo( from, 1038325 ); // You cannot dismiss me while I am holding your goods.
				return;
			}

			VendorManager vm = m_Manager;

			if ( AutoAddTileOnDismiss && vm.FindMobileInMall( (Mobile)this, vm ) )
			{
				Point3D loc = this.Location;
				Map map = this.Map;

				base.Dismiss( from );

				Item tile = new CommVendorTile( vm.TileID, vm.TileHue );
				tile.MoveToWorld( loc, map );
			}
			else
				base.Dismiss( from );
		}

		public TimeSpan GetCommDelay()
		{
			double rand = (double)Utility.Random( 10, 20 );
			TimeSpan delay = TimeSpan.FromMinutes( rand );

			return delay;
		}

		public override void OnDoubleClick( Mobile from )
		{
			if ( from == Owner || from.AccessLevel >= AccessLevel.GameMaster || EnableVendorBackpack )
				base.OnDoubleClick( from );

			if ( m_ShopItems != null && m_ShopItems.Count > 0 )
				VendorManager.SellHardItem( from, (Mobile)this );
		}

		public void AddBidItems( Mobile from, BidByItem bbi )
		{
			if ( m_BidItems == null )
				m_BidItems = new List<BidByItem>();

			m_BidItems.Add( bbi );
		}

		public void ConstructCommTimer()
		{
			m_Timer = new InternalTimer( (Mobile)this, m_Delay );
			m_Timer.Start();
		}

		public override void GetContextMenuEntries( Mobile from, List<ContextMenuEntry> list )
		{
			if ( from.Alive && !Deleted && ( from.AccessLevel >= AccessLevel.GameMaster || IsOwner( from ) ) )
			{
				list.Add( new SetupVendorEntry( from, this ) );
				list.Add( new AddVendorItemEntry( from, this ) );
			}

			if ( from.Alive && !Deleted )
			{
				list.Add( new BuyVendorItemEntry( from, this ) );
				list.Add( new ListBidEntry( from, this ) );
			}

			base.GetContextMenuEntries( from, list );
		}

		public override void Serialize( GenericWriter writer )
		{
			base.Serialize( writer );
			writer.Write( (int) 1 ); // version

			writer.Write( (Mobile) m_Manager );
			writer.Write( (Item) m_HoldBox );
			writer.Write( (Item) m_ShowBox );
			writer.Write( (string) m_Currency.Name );
			writer.Write( (int) m_CommRate );
			writer.Write( (bool) m_IsComm );
			writer.Write( (bool) m_AllowBid );
			writer.Write( (bool) m_BondOnly );

			// version 1
			writer.Write( (int) m_LastHold );

			writer.Write( (int) m_CInfo.m_itemID );
			writer.Write( (int) m_CInfo.m_hue );
			writer.Write( (bool) m_CInfo.m_dupeicon );

			if ( m_ShopItems != null && m_ShopItems.Count > 0 )
			{
				writer.Write( (int) m_ShopItems.Count );

				foreach ( DictionaryEntry de in m_ShopItems )
				{
					writer.Write( (Item) de.Key );

					TradeItem ti = (TradeItem)de.Value;

					writer.Write( (Item)ti.ItemObj );
					writer.Write( (int)ti.Price );
					writer.Write( (string)ti.Description );
				}
			}
			else
				writer.Write( (int) 0 );

			if ( m_BidItems != null && m_BidItems.Count > 0 )
			{
				writer.Write( (int) m_BidItems.Count );

				for ( int i = 0; i < m_BidItems.Count; i++ )
				{
					BidByItem bbi = (BidByItem)m_BidItems[i];

					writer.Write( (Item) bbi.m_itemobj );
					writer.Write( (TimeSpan) bbi.m_duration );
					writer.Write( (DateTime) bbi.m_start );

					if ( bbi.m_byprice != null && bbi.m_byprice.Count > 0 )
					{
						writer.Write( (int) bbi.m_byprice.Count );
						foreach ( BidItem bi in bbi.m_byprice )
						{
							writer.Write( (Mobile) bi.m_bider );
							writer.Write( (int) bi.m_bid );
						}
					}
					else
						writer.Write( (int) 0 );
				}
			}
			else
				writer.Write( (int) 0 );

			writer.Write( (TimeSpan) m_Delay );
		}

		public override void Deserialize( GenericReader reader )
		{
			base.Deserialize( reader );
			int version = reader.ReadInt();

			m_Manager = (VendorManager)reader.ReadMobile();
			m_HoldBox = (VendorBox)reader.ReadItem();
			m_ShowBox = (VendorBox)reader.ReadItem();
			m_Currency = ScriptCompiler.FindTypeByName( reader.ReadString() );
			m_CommRate = reader.ReadInt();
			m_IsComm = reader.ReadBool();
			m_AllowBid = reader.ReadBool();
			m_BondOnly = reader.ReadBool();

			if ( version > 0 )
				m_LastHold = reader.ReadInt();

			m_CInfo = new CurrencyInfo( reader.ReadInt(), reader.ReadInt(), reader.ReadBool() );

			m_ShopItems = new Hashtable();
			for ( int i = reader.ReadInt(); i > 0; i-- )
			{
				Item item = reader.ReadItem();
				TradeItem ti = new TradeItem( (object)reader.ReadItem(), reader.ReadInt(), reader.ReadString() );

				m_ShopItems[item] = ti;
			}

			m_BidItems = new List<BidByItem>();

			for ( int i = reader.ReadInt(); i > 0; i-- )
			{
				BidByItem bbi = new BidByItem( (object)reader.ReadItem(), reader.ReadTimeSpan() );
				bbi.m_start = reader.ReadDateTime();

				for ( int j = reader.ReadInt(); j > 0; j-- )
				{
					bbi.m_lowest = 0;
					bbi.Add( reader.ReadMobile(), reader.ReadInt() );
				}

				m_BidItems.Add( bbi );
			}

			m_Delay = reader.ReadTimeSpan();

			if ( m_IsComm )
			{
				if ( DailyPayTimer != null )
					DailyPayTimer.Stop();

				DailyPayTimer = new Timer( TimeSpan.FromDays( 9999.0 ) );

				m_Timer = new InternalTimer( (Mobile)this, m_Delay );
				m_Timer.Start();
			}
		}

		private class ListBidEntry : ContextMenuEntry
		{
			private Mobile m_Mobile;
			private Mobile m_Vendor;

			public ListBidEntry( Mobile from, Mobile vendor ) : base( 0156, 3 )
			{
				m_Mobile = from;
				m_Vendor = vendor;

				Enabled = ( m_Mobile.Alive && m_Vendor is IHardsellable 
					&& ((IHardsellable)m_Vendor).BidItems != null && (((IHardsellable)m_Vendor).BidItems).Count > 0 );
			}

			public override void OnClick()
			{
				VendorManager.ListBidItems( m_Mobile, m_Vendor );
			}
		}

		private class SetupVendorEntry : ContextMenuEntry
		{
			private Mobile m_Mobile;
			private Mobile m_Vendor;

			public SetupVendorEntry( Mobile from, Mobile vendor ) : base( 0097, 3 )
			{
				m_Mobile = from;
				m_Vendor = vendor;

				Enabled = ( m_Mobile.Alive && ( m_Mobile.AccessLevel >= AccessLevel.GameMaster 
					|| ((CommissionedVendor)m_Vendor).IsOwner( m_Mobile ) ) );
			}

			public override void OnClick()
			{
				m_Mobile.CloseGump( typeof( VendorSetupGump ) );
				m_Mobile.SendGump( new VendorSetupGump( m_Mobile, m_Vendor ) );
			}
		}

		private class AddVendorItemEntry : ContextMenuEntry
		{
			private Mobile m_Mobile;
			private Mobile m_Vendor;

			public AddVendorItemEntry( Mobile from, Mobile vendor ) : base( 0175, 3 )
			{
				m_Mobile = from;
				m_Vendor = vendor;

				Enabled = ( m_Mobile.Alive && ( m_Mobile.AccessLevel >= AccessLevel.GameMaster 
					|| ((CommissionedVendor)m_Vendor).IsOwner( m_Mobile ) ) );
			}

			public override void OnClick()
			{
				VendorManager.AddHardItem( m_Mobile, m_Vendor );
			}
		}

		private class BuyVendorItemEntry : ContextMenuEntry
		{
			private Mobile m_Mobile;
			private Mobile m_Vendor;

			public BuyVendorItemEntry( Mobile from, Mobile vendor ) : base( 0146, 3 )
			{
				m_Mobile = from;
				m_Vendor = vendor;

				Enabled = ( m_Mobile.Alive && m_Vendor is IHardsellable && ((IHardsellable)m_Vendor).HoldBox != null 
					&& ((IHardsellable)m_Vendor).ShopItems != null && (((IHardsellable)m_Vendor).ShopItems).Count > 0 );
			}

			public override void OnClick()
			{
				VendorManager.SellHardItem( m_Mobile, m_Vendor );
			}
		}

		private class InternalTimer : Timer
		{
			private Mobile m_vendor;

			public InternalTimer( Mobile vendor, TimeSpan delay ) : base( delay, delay )
			{
				m_vendor = vendor;
			}

			protected override void OnTick()
			{
				CommissionedVendor cv = m_vendor as CommissionedVendor;

				if ( cv.IsComm && cv.DailyPayTimer != null )
				{
					cv.DailyPayTimer.Stop();
					cv.DailyPayTimer = new Timer( TimeSpan.FromDays( 9999.0 ) );
				}

				if ( !cv.Deleted && cv.IsComm && cv.CommRate > 0 && cv.CommRate <= 100 && cv.HoldGold > cv.LastHold )
				{
					int left = cv.HoldGold - cv.LastHold;
					int comm = (int)( left * cv.CommRate / 100 );

//					Console.WriteLine( "Before - LastHold: {0}, HoldGold: {1}.", cv.LastHold, cv.HoldGold );		// debug code

					cv.HoldGold -= comm;
					cv.LastHold = cv.HoldGold;

//					Console.WriteLine( "After - LastHold: {0}, HoldGold: {1}.", cv.LastHold, cv.HoldGold );		// debug code
				}
				else if ( !cv.Deleted && cv.HoldGold < cv.LastHold )		// owner claimed gold
				{
					cv.LastHold = cv.HoldGold;
//					Console.WriteLine( "It doesn't charge. Last: {0}, Hold: {1}", cv.LastHold, cv.HoldGold );		// debug code
				}
			}
		}

	}
}